--
-- Author: shunguo.chen
-- Date: 2018-04-18 14:36:27
--
-- GuideModel
--
-- 引导分两部分，正常引导和支线任务引导
-- 正常引导，每一步服务器会上传服务器记录，做过不会再做
-- 支线任务引导，不会存储服务器，随支线任务生命周期，点击支线任务开始，任务结束引导也结束
-- 数据初始化会过滤出 等待激活的引导和当前激活的引导，当前激活分为一般引导（_curIds）和支线引导（_curSubIds）
-- 等待激活的引导，由等级，主线任务，支线任务，前置引导等激活
-- 当前激活的引导，会在业务中的触发点去试着触发（checkGuide）
-- 

local Guide = import(".Guide")

local fileName = string.getModeName(...)
local ClassRef = class(fileName, app.mvc.ModelBase)

function ClassRef:ctor(data)
    self.super.ctor(self, data)
end

function ClassRef:init()
	-- 未完成guide集合
	self._guideDict = nil
	-- 任务激活 等待id集合
	self._taskWaitDict = nil
	-- 支线任务激活 等待id集合
	self._sideTaskWaitDict = nil
	-- 等级激活 等待id集合
	self._levelWaitDict = nil
	-- 激活状态的引导id
	self._curIds = nil
	-- 激活状态的支线引导id
	self._curSubIds = nil

	-- 最大引导id
	self._maxGuideId = 101

	self._isInit = false
end

-- 测试使用，把<id的引导都设置成完成，并设置curIds={id}
function ClassRef:testGuide(id)
	if DEBUG == 0 then 
		for i=101, self._maxGuideId do
			UD:saveKeyValueForTutorial(i, "1")
		end
		self._taskWaitDict = {}
		self._sideTaskWaitDict = {}
		self._levelWaitDict = {}
		self._curIds = {}
		self._curSubIds = {}
		return 
	end

	UD.kvStores.tutorial = {}
	if id <= 101 then
		id = 101
	else
		for i=101,id-1 do
			UD:saveKeyValueForTutorial(id, "1")
		end
	end
	local guide = self:getGuideById(id)
	if guide:isBelongSideTask() then
		self._curSubIds = {id}
	else
		self._curIds = {id}
	end
	app:sendMsg("GuideController", "tryToTriggerGuide", guide)
end

function ClassRef:isGuideComp(id)
	-- for DEBUG test
	if DEBUG > 0 then return false end
	if GUIDE_TEST_ID then
		return id < GUIDE_TEST_ID
	end

	-- 有完成标志，全部都算完成
	if UD:isGuideOver() then return true end

	local conf = GD:getGuideConf(id)
	local belongTask = conf.belongTask
	if belongTask then
		-- 有属于的任务，任务完成时引导完成，不需要存服务器
		local tp = tonumber(string.sub(belongTask, 1, 1))
		local taskId = tonumber(string.sub(belongTask, 3))
		if tp == 1 then
			-- 主线任务
			return UD:isMainTaskFinished(taskId)
		else
			-- 支线任务
			return UD:isSideTaskComp(taskId)
		end
	else
		-- 存储在服务器的引导
		local value = UD:getValueFromTutorialByKey(id)
		return value ~= nil
	end
end

function ClassRef:initData()
	-- 未完成guide集合
	local guideDict = {}
	-- 任务等待激活guide集合
	local taskWaitDict = {}
	-- 支线任务等待激活guide集合
	local sideTaskWaitDict = {}
	-- 等级等待激活guide集合
	local levelWaitDict = {}
	-- 激活guideIds
	local curIds = {}

	local level = UD:getLevel()
	local confs = GD:getGuideConfs()
	local maxId = 101
	for i,conf in ipairs(confs) do
		local id = tonumber(conf.ID)
		maxId = math.max(id, maxId)
		if not self:isGuideComp(id) then
			-- 缓存 guideDict
			local guide = Guide.new(conf)
			guideDict[id] = guide

			-- 触发条件
			local activate = conf.activate
			local tp = tonumber(string.sub(activate, 1, 1))
			local val = tonumber(string.sub(activate, 3))
			guide:setActivateData(tp, val)

			if tp == 1 then
				-- 接受主线任务激活
				if UD:isMainTaskToken(val) then
					-- 已激活
					curIds[#curIds + 1] = id
				else
					-- 未激活, 按任务id为key保存任务激活的引导
					taskWaitDict[val] = taskWaitDict[val] or {}
					table.insert(taskWaitDict[val], id)
				end
			elseif tp == 2 then
				-- 等级激活
				if val <= level then
					-- 已激活
					curIds[#curIds + 1] = id
				else
					-- 未激活
					levelWaitDict[val] = levelWaitDict[val] or {}
					table.insert(levelWaitDict[val], id)
				end
			elseif tp == 3 then
				-- 其他引导后续激活
				-- 本身未完成，前置已经完成，该引导处于激活状态
				if self:isGuideComp(val) then
					curIds[#curIds + 1] = id
				end
			elseif tp == 4 then
				-- 打开界面激活
				curIds[#curIds + 1] = id
			elseif tp == 5 then
				-- 支线任务触发, 属于激活状态
				sideTaskWaitDict[val] = sideTaskWaitDict[val] or {}
				table.insert(sideTaskWaitDict[val], id)
			end
		end
	end

	self._guideDict = guideDict
	self._taskWaitDict = taskWaitDict
	self._sideTaskWaitDict = sideTaskWaitDict
	self._levelWaitDict = levelWaitDict
	self._curIds = curIds
	self._curSubIds = {}
	self._isInit = true
	self._maxGuideId = maxId

	-- for test
	if DEBUG > 0 then
		for i=101, self._maxGuideId do
			UD:saveKeyValueForTutorial(i, "1")
		end
		self._taskWaitDict = {}
		-- self._sideTaskWaitDict = {}
		self._levelWaitDict = {}
		self._curIds = {}
	end

	-- dump(self._guideDict, "GuideModel.initData guideDict")
	-- dump(self._curIds, "GuideModel.initData curIds")
	-- dump(self._taskWaitDict, "GuideModel.initData taskWaitDict")
	-- dump(self._sideTaskWaitDict, "GuideModel.initData sideTaskWaitDict")
	-- dump(self._levelWaitDict, "GuideModel.initData levelWaitDict")
end

function ClassRef:getGuideById(id)
	if not id then return nil end
	return self._guideDict[id]
end

function ClassRef:activateSideTaskGuide(taskId)
	self:activateGuide(Enums.GuideActivateType.sideTask, taskId)
end

-- tp = Enums.GuideActivateType
-- param = 参数
function ClassRef:activateGuide(tp, param)
	if not self._isInit then return end

	-- print("GuideModel:activateGuide", tp, param)
	if tp == Enums.GuideActivateType.level then
		-- param = level
		local ary = self._levelWaitDict[param]
		if ary and #ary > 0 then
			for i,id in ipairs(ary) do
				self._curIds[#self._curIds + 1] = id
				local guide = self:getGuideById(id)
				app:sendMsg("GuideController", "tryToTriggerGuide", guide)
			end
			self._levelWaitDict[param] = nil
		end
	elseif tp == Enums.GuideActivateType.mainTask then
		-- param = tasks
		for i,task in pairs(param) do
			-- print("activateGuide", task.id)
			if task:isMain() then
				local ary = self._taskWaitDict[task.id]
				if ary and #ary > 0 then
					for i,id in ipairs(ary) do
						self._curIds[#self._curIds + 1] = id
						local guide = self:getGuideById(id)
						app:sendMsg("GuideController", "tryToTriggerGuide", guide)
					end
					self._taskWaitDict[task.id] = nil
				end
			end
		end
	elseif tp == Enums.GuideActivateType.sideTask then
		-- 支线任务 param = taskId
		self._curSubIds = {}
		local ary = self._sideTaskWaitDict[param]
		if ary and #ary > 0 then
			for i,id in ipairs(ary) do
				self._curSubIds[#self._curSubIds + 1] = id
				local guide = self:getGuideById(id)
				app:sendMsg("GuideController", "tryToTriggerGuide", guide)
			end
		end
	end
end

-- 试图触发当前已有的引导
function ClassRef:tryToTriggerGuides()
	for i=#self._curSubIds,1,-1 do
		local guide = self:getGuideById(self._curSubIds[i])
		app:sendMsg("GuideController", "tryToTriggerGuide", guide)
	end
	for i=#self._curIds,1,-1 do
		local guide = self:getGuideById(self._curIds[i])
		app:sendMsg("GuideController", "tryToTriggerGuide", guide)
	end
end

-- 主线任务完成时，依赖主线任务的引导都算完成，且移除引导相关ui
function ClassRef:onTaskComp(tasks)
	if not self._curIds then return end
	if #self._curIds == 0 then return end

	for i,task in pairs(tasks) do
		if task:isMain() and task:isFinished() then
			for _,id in ipairs(self._curIds) do
				local guide = self:getGuideById(id)
				if guide and task.id == guide.belongTaskId then
					app:sendMsg("GuideController", "compTaskGuide", guide)
				end
			end
		end
	end
end

-- 支线任务完成时，依赖支线任务的引导都算完成，且移除引导相关ui
function ClassRef:onSideTaskComp(taskId)
	if not self._curSubIds then return end
	if #self._curSubIds == 0 then return end

	for _,id in ipairs(self._curSubIds) do
		local guide = self:getGuideById(id)
		if guide and taskId == guide.belongTaskId then
			app:sendMsg("GuideController", "compTaskGuide", guide)
		end
	end

	self._sideTaskWaitDict[taskId] = nil
end

-- params = {
-- 	scene = funcId,
-- 	parent = node,
-- 	getFunc = func
-- }
function ClassRef:checkGuide(params)
	-- print(debug.traceback("", 1))
	-- print("GuideModel:checkGuide", params.scene)

	if self._curSubIds and #self._curSubIds > 0 then
		for i,id in ipairs(self._curSubIds) do
			local guide = self:getGuideById(id)
			if guide and guide.scene == params.scene then
				if guide.delay > 0 then
					scheduler.newCron(function()
						app:sendMsg("GuideController", "triggerGuide", guide, params)
					end, guide.delay)
				else
					app:sendMsg("GuideController", "triggerGuide", guide, params)
				end
			end
		end
	end

	if self._curIds and #self._curIds > 0 then
		for i=#self._curIds,1,-1 do
			local guide = self:getGuideById(self._curIds[i])
			if guide and guide.scene == params.scene then
				if guide.delay > 0 then
					scheduler.newCron(function()
						app:sendMsg("GuideController", "triggerGuide", guide, params)
					end, guide.delay)
				else
					app:sendMsg("GuideController", "triggerGuide", guide, params)
				end
			end
		end
	end

end

-- 取消引导，只在支线任务引导时有效
function ClassRef:cancelGuide(guide)
    if not guide then return end
    if not guide:isBelongSideTask() then return end

    self._curSubIds = {}
end

-- 取消支线引导
function ClassRef:cancelSideTaskGuide()
	self._curSubIds = {}
end

------------------------ receive ------------------------


------------------------ request ------------------------
function ClassRef:reqCompGuide(id, cb)
	local guide = self:getGuideById(id)
	local isBelongSideTask = guide:isBelongSideTask()
	if isBelongSideTask then
		table.removebyvalue(self._curSubIds, id)
	else
		table.removebyvalue(self._curIds, id)
	end

	-- 循环检测下个引导是否已经完成
	local nextGuide = self:getGuideById(guide.nextId)
	while nextGuide do
		if self:isGuideComp(nextGuide.id) then
			nextGuide = self:getGuideById(nextGuide.nextId)
		else
			if isBelongSideTask then
				table.insertUnique(self._curSubIds, nextGuide.id)
			else
				table.insertUnique(self._curIds, nextGuide.id)
			end
			break
		end
	end

	executeFunc(cb, nextGuide)

	-- 不属于支线任务，存储到服务器
	if not isBelongSideTask then
		UD:saveKeyValueForTutorial(id, "1")

		local params = {}
		params.category = "tutorial"
		params.item = {key=tostring(id), value="1"}
		app.session:request("kvstore.C2S_PutKVItem", params, function(msg)
				-- print("kvstore.C2S_PutKVItem", msg.ok)
			end)
	end
end

return ClassRef
